package bu.met.cs664;

import java.util.Arrays;

public class Board {
	
	static final int NUM_COLS       = 3;
	static final int NUM_ROWS       = NUM_COLS;
	static final int MAX_SELECTIONS = NUM_ROWS * NUM_COLS;
	
	private char[] currentState = {' ',' ',' ',' ',' ',' ',' ',' ',' '};
	private int lastSelection = MAX_SELECTIONS;
	
	public Board(Board board) {
		this.currentState = board.getCurrentState().clone();
	}
	
	public Board() {
	}
	
	public char[] getCurrentState(){
		return currentState;
	}
			
	/**
	 * Selects a square for a player.
	 * 
	 * @param	player	current player
	 * 
	 * @param	i		zero-based index of the square
	 */
	public void select(Player player, int i) {
		assert((player.isPlayerX()) || (player.isPlayerO()));
		assert(i < MAX_SELECTIONS && i > 0);
		
		currentState[i] = player.toChar();
		lastSelection = i;
	}
	
	/**
	 * Determines if the player in question has won with this game board.
	 * 
	 * @param	player	current player
	 * 
	 * @return 	true if the player has won, false otherwise
	 */
	public boolean won(Player player) {
		assert((player.isPlayerX()) || (player.isPlayerO()));
		
		Board rotated = this.createRotated();
		char playerSymbol = player.toChar();
		return (this.wonRow(playerSymbol)
				|| this.wonDiagonal(playerSymbol)
				|| rotated.wonRow(playerSymbol)
				|| rotated.wonDiagonal(playerSymbol));		
	}
	
	/**
	 * Returns a new board that is rotated 90 degrees
	 * 
	 * @return rotated board
	 */
	public Board createRotated() {
		
		Board rotated = new Board();
		for (int i=0; i < Board.MAX_SELECTIONS; i++) {
			int row = i/NUM_ROWS;
			int col = i%NUM_COLS;
			
			int newRow = col;
			int newCol = NUM_ROWS-row-1;
			int position = NUM_ROWS*newRow + newCol;
			rotated.currentState[position] = this.currentState[i];
		}
		
		return rotated;		
	}
	
	private boolean wonRow(char playerSymbol) {
		int playerCount = 0;
		for (int i = 0; i < Board.MAX_SELECTIONS; i++) {
			//reset player count
			if (i%NUM_ROWS == 0) {
				playerCount = 0;
			}
			if (this.currentState[i] == playerSymbol) {
				playerCount++;
			} 
			if (playerCount == NUM_ROWS) {
				return true;
			}
		}
		return false;		
	}
	
	private boolean wonDiagonal(char playerSymbol) {
		int playerCount = 0;
		for (int i = 0; i < NUM_ROWS; i++) {
			int position = (((NUM_COLS) + 1) * i);
			if (this.currentState[position] == playerSymbol) {
				playerCount++;
			} else {
				break;
			}
			if (playerCount == Math.min(NUM_ROWS, NUM_COLS)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Can the player block a next-turn win from his/her opponent?
	 * 
	 * @param	player		current player
	 * @param   opponent 	opposing player
	 * 
	 * @return 	true if the player can block a win, false otherwise
	 */
	public boolean blockWin(Player player, Player opponent) {
		assert((player.isPlayerX()) || (player.isPlayerO()));
		assert(won(opponent) == false);

		// Pretend the opponent got this spot instead of our player.
		currentState[lastSelection] = opponent.toChar();
		
		boolean blockWin = won(opponent);
		
		// Clean up.
		currentState[lastSelection] = player.toChar();
		
		return blockWin;
	}
	
	/**
	 * Provides the open squares on the board.
	 * 
	 * @return	an array of integers identifying open squares
	 */
	public int[] getAvailableSelections() {
		int[] available = new int[MAX_SELECTIONS]; 
		int availSize = 0;

		for (int i = 0; i < MAX_SELECTIONS; i++) {
			if (this.getCurrentState()[i] == ' ') {
				available[availSize++] = i;
			}
		}
		return available = Arrays.copyOf(available, availSize);
	}
	
	public boolean isAvailable(int position) {
		if (currentState[position] == ' ') {
			return true;
		} else {
			return false;
		}
	}
	
	public boolean isValid(int position) {
		return position >= 0 && position < MAX_SELECTIONS;
	}
	
	/**
	 * Returns whether all the spaces are filled
	 * @return
	 */
	public boolean isBoardFilled(){ 
		if (this.getAvailableSelections().length == 0) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Has someone won or is the board completely filled?
	 * 
	 * @return	true if the game is over, false otherwise
	 */
	public boolean isGameOver(Player player1, Player player2) {
		
		return this.won(player1) || this.won(player2) || this.isBoardFilled();
	}
}
